package cn.hg.solon.youcan.system.provider;

import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.common.util.PasswordUtil;
import cn.hg.solon.youcan.flex.util.QueryWrapperUtil;
import cn.hg.solon.youcan.system.entity.SysUser;
import cn.hg.solon.youcan.system.entity.SysUserRoleMapping;
import cn.hg.solon.youcan.system.entity.User;
import cn.hg.solon.youcan.system.mapper.SysUserMapper;
import cn.hg.solon.youcan.system.mapper.SysUserRoleMappingMapper;
import cn.hg.solon.youcan.system.service.DeptService;
import cn.hg.solon.youcan.system.service.UserService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.query.RawQueryCondition;
import com.mybatisflex.solon.service.impl.ServiceImpl;
import org.apache.ibatis.solon.annotation.Db;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.core.util.RandomUtil;
import org.dromara.hutool.db.PageResult;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.Tran;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static cn.hg.solon.youcan.system.entity.table.SysDeptTableDef.SYS_DEPT;
import static cn.hg.solon.youcan.system.entity.table.SysUserRoleMappingTableDef.SYS_USER_ROLE_MAPPING;
import static cn.hg.solon.youcan.system.entity.table.SysUserTableDef.SYS_USER;
import static com.mybatisflex.core.query.QueryMethods.select;

/**
 * @author 胡高
 */
@Component
public class SysUserProvider extends ServiceImpl<SysUserMapper, SysUser> implements UserService {

    @Db
    private SysUserRoleMappingMapper userRoleMappingMapper;

    @Inject
    private DeptService deptService;

    @Tran
    @Override
    public void assignRole(User user, List<Integer> roleIds) {
        /*
         * 删除所有角色
         */
        this.userRoleMappingMapper.deleteByCondition(SYS_USER_ROLE_MAPPING.USER_ID.eq(user.getId()));

        /*
         * 重新建立用户角色
         */
        List<SysUserRoleMapping> beanList = new ArrayList<>();
        for (int id : roleIds) {
            SysUserRoleMapping bean = new SysUserRoleMapping();
            bean.setUserId(user.getId());
            bean.setRoleId(id);
            beanList.add(bean);
        }

        if (CollUtil.isNotEmpty(beanList)) {
            this.userRoleMappingMapper.insertBatch(beanList);
        }
    }

    /**
     * <pre>
     * SELECT *
     * FROM `sys_user`
     * WHERE `status` = ?
     *     AND (`dept_id` = ? OR `dept_id` IN (
     *             SELECT `id` FROM `sys_dept` WHERE FIND_IN_SET (?, ancestors ) AND `del` = ?
     *         )
     *     )
     *     AND ( `account` LIKE ? OR `nickname` LIKE ? OR `email` LIKE ? OR `phone` LIKE ? )
     *     AND `del` = ?
     * </pre>
     *
     * @return
     */
    private QueryWrapper buildQuery(org.dromara.hutool.db.Page page, Map<String, Object> paraMap) {
        String word = (String) paraMap.get("word");
        String status = (String) paraMap.get("status");
        Integer deptId = (Integer) paraMap.get("deptId");

        QueryWrapper query = QueryWrapper.create()
                .and(SYS_USER.DEPT_ID.eq(deptId).when(ObjUtil.isNotNull(deptId))
                        .or(SYS_USER.DEPT_ID.in(select(SYS_DEPT.ID)
                                        .from(SYS_DEPT)
                                        .where(new RawQueryCondition("FIND_IN_SET (?, " + SYS_DEPT.ANCESTORS.getName() + ")", deptId).when(ObjUtil.isNotNull(deptId)))
                                )
                        )
                )
                .and(SYS_USER.STATUS.eq(status).when(StrValidator.isNotBlank(status)))
                .and(SYS_USER.ACCOUNT.like(word).when(StrValidator.isNotBlank(word))
                        .or(SYS_USER.NICKNAME.like(word).when(StrValidator.isNotBlank(word)))
                        .or(SYS_USER.EMAIL.like(word).when(StrValidator.isNotBlank(word)))
                        .or(SYS_USER.PHONE.like(word).when(StrValidator.isNotBlank(word)))
                );

        return QueryWrapperUtil.applyOrderBy(query, page);
    }

    @Override
    public boolean checkUnique(User bean) {
        // 查找已存在记录
        QueryWrapper query = QueryWrapper.create()
                .where(SYS_USER.ACCOUNT.eq(bean.getAccount())
                        .and(SYS_USER.ID.ne(bean.getId()).when(ObjUtil.isNotNull(bean.getId())))
                );

        return ObjUtil.isNull(this.getOne(query));
    }

    @Tran
    @Override
    public boolean delete(List<Integer> idList) {
        if (CollUtil.contains(idList, Integer.valueOf(1))) {
            throw new ServiceException("初始的 超级管理员 不允许删除！");
        }
        return this.getMapper().deleteBatchByIds(idList) > 0;
    }

    @Override
    public SysUser get(Integer userId) {
        return this.getMapper().selectOneById(userId);
    }

    @Override
    public SysUser getByAccount(String account) {
        QueryWrapper query = QueryWrapper.create().where(SYS_USER.ACCOUNT.eq(account));
        return this.getOne(query);
    }

    @Tran
    @Override
    public boolean insert(User bean, List<Integer> roleIds) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException("登录账号已经存在，请更换其它值！");
        }
        // 初始化密码
        bean.setSalt(RandomUtil.randomString(20));
        bean.setPassword(PasswordUtil.genSecretPassword(bean.getSalt(), bean.getPassword()));

        SysUser cloneBean = BeanUtil.copyProperties(bean, SysUser.class);

        boolean ret = this.getMapper().insert(cloneBean) > 0;

        // 指派角色
        this.assignRole(cloneBean, roleIds);


        return ret;
    }

    @Override
    public List<SysUser> listBy(Map<String, Object> paraMap) {
        return this.list(this.buildQuery(null, paraMap));
    }

    @Override
    @Tran
    public PageResult<SysUser> pageBy(org.dromara.hutool.db.Page page, Map<String, Object> paraMap) {
        Page<SysUser> pageList = this.getMapper().paginate(Page.of(page.getPageNumber(), page.getPageSize()),
                this.buildQuery(page, paraMap));

        PageResult<SysUser> result = new PageResult<>();
        result.addAll(pageList.getRecords());
        result.setTotal(ConvertUtil.toInt(pageList.getTotalRow()));

        return result;
    }

    @Tran
    @Override
    public void resetPassword(User bean, String oldPassword, String newPassword, User editor) {
        if (!PasswordUtil.validatePassword(bean.getSalt(), oldPassword, bean.getPassword())) {
            throw new ServiceException("旧密码不正确！");
        }

        this.resetPassword(bean, newPassword, editor);
    }

    @Tran
    @Override
    public void resetPassword(User bean, String password, User editor) {
        bean.setPassword(PasswordUtil.genSecretPassword(bean.getSalt(), password));
        bean.setEditedDatetime(DateUtil.now());

        SysUser cloneBean = BeanUtil.copyProperties(bean, SysUser.class);

        this.getMapper().update(cloneBean);
    }

    @Tran
    @Override
    public boolean update(User bean) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException("登录账号已经存在，请更换其它值！");
        }

        SysUser cloneBean = BeanUtil.copyProperties(bean, SysUser.class);

        return this.getMapper().update(cloneBean) > 0;
    }

}
